1、 SPI 框架
	SPI 框架包括以下几个部分：
	1） SPI 核心；管理 SPI 控制器驱动驱动、 SPI 设备驱动、 SPI 设备，此部分由linux提供
	2） SPI 控制器驱动：用于驱 SOC 上的SPI控制器，此部分由主控芯片厂家提供
	3） SPI 设备：用于描述 SPI 设备信息和此设备对SPI总线的配置，一般在设备树中编写。
	4） SPI 设备驱动：用于驱动 SPI 总线上的设备，一移植厂家驱动或自己编写
	5） spidev ：一个通用的SPI设备驱动，提供一种用户空间访问SPI总线的功能（需要设备树支持）
2、 SPI 控制器驱动
	对象 struct spi_controller 表示一个 SPI 控制器，其核心成员如下：
	//表示继承于struct device，其中的of_node需要指定，否则无法解析设备树
	struct device dev;
	//总线编号
	s16 bus_num;
	//片选数量，从设备的片选号不能大于这个数量
	u16 num_chipselect;
	//控制器所支持的模式
	u32 mode_bits;
	//最小传输速率
	u32 min_speed_hz;
	//最大传输速率
	u32 max_speed_hz;
	//标志，表示控制器的一些特性
	u16 flags;
	//指示此控制器是否时从设备
	bool slave;
	//配置SPI控制器
	int (*setup)(struct spi_device *spi);
	//设置CS的时序
	void (*set_cs_timing)(struct spi_device *spi, u8 setup_clk_cycles, 
		u8 hold_clk_cycles, u8 inactive_clk_cycles);
	//SPI数据包传输
	int (*transfer)(struct spi_device *spi, struct spi_message *mesg);
	//维护 queue 的自旋锁
	spinlock_t queue_lock;
	//管理 SPI 控制器需要传输的 spi_message，一个 spi_message 表示一系列需要传输的数据
	struct list_head queue;
	//SPI 控制器正在传输的 spi_message
	struct spi_message *cur_msg;
	SPI 控制器驱动的核心就是完成对 struct spi_controller 的分配和初始化，然后将其添加到系统中，如下是
分配 SPI 控制器并向系统添加和删除 SPI 控制器驱动的函数
	//分配SPI控制器， spi_alloc_master 分配主控制器， spi_alloc_slave 分配从控制器
	struct spi_controller *spi_alloc_master(struct device *host, unsigned int size)
	struct spi_controller *spi_alloc_slave(struct device *host, unsigned int size)
	//释放SPI控制器
	void spi_controller_put(struct spi_controller *ctlr)
	#define spi_master_put(_ctlr) spi_controller_put(_ctlr)
	//注册SPI控制器
	int spi_register_master(struct spi_controller *ctlr)
	int spi_register_controller(struct spi_controller *ctlr)
	int devm_spi_register_masterstruct device *dev, struct spi_controller *ctlr);
	int devm_spi_register_controllerstruct device *dev, struct spi_controller *ctlr);
	//注销SPI控制器
	void spi_unregister_master(struct spi_controller *ctlr)
3、 SPI 设备驱动
	SPI 设备用 struct spi_device 表示，它用于描述一个 SPI 设备， SPI 设备驱动用 struct spi_driver 表示，它用
于描述 SPI 设备的驱动。
	struct spi_device 的核心成员如下：
		//继承的device对象
		struct device dev;
		//所属控制器
		struct spi_controller *controller;
		//最大总线频率
		u32 max_speed_hz;
		//片选号，与控制器的片选列表对应
		u8 chip_select;
		//总线模式
		u32 mode;
		//片选引脚，不使用时为-ENOENT
		int cs_gpio;
		//驱动程序的名称或别名
		char modalias[SPI_NAME_SIZE];
		//强制匹配字符串
		const char *driver_override;
	struct spi_driver 的核心成员如下：
		//ID匹配表
		const struct spi_device_id *id_table;
		//设备和驱动匹配成功执行
		int (*probe)(struct spi_device *spi);
		//设备或驱动卸载执行
		int (*remove)(struct spi_device *spi);
		//继承的device_driver
		struct device_driver driver;
	//注册SPI设备驱动
	int spi_register_driver(struct spi_driver *sdrv)
	//注销SPI设备驱动
	void spi_unregister_driver(struct spi_driver *sdrv)
4、 SPI 设备和驱动匹配过程
	如下时SPI设备和驱动的匹配过程：
	static int spi_match_device(struct device *dev, struct device_driver *drv)
	{
		const struct spi_device	*spi = to_spi_device(dev);
		const struct spi_driver	*sdrv = to_spi_driver(drv);

		//采用driver_override进行强制匹配
		if (spi->driver_override)
			return strcmp(spi->driver_override, drv->name) == 0;

		//设备树匹配
		if (of_driver_match_device(dev, drv))
			return 1;

		//ACPI匹配
		if (acpi_driver_match_device(dev, drv))
			return 1;

		//id_table匹配
		if (sdrv->id_table)
			return !!spi_match_id(sdrv->id_table, spi);

		//采用驱动程序的名称匹配
		return strcmp(spi->modalias, drv->name) == 0;
	}
5、 SPI 控制器注册过程
	构造并初始化一个 struct spi_controller 对象
		调用 spi_register_controller 注册 SPI 控制器
			使用 spi_controller_check_ops 检查必要参数是否配置正确
			通过 idr_alloc 将 SPI 控制器放入到一个 idr 对象中（如果 bus_num 无效会尝试从设备树中获取）
			初始化 struct spi_controller 各种资源
			从设备树中解析片选引脚
			使用 device_add 将 struct spi_controller 的 dev 添加到内核
			将 struct spi_controller 放入 spi_controller_list 链表中
			调用 of_register_spi_devices 解析 SPI 设备树
				遍历 spi 控制器的设备树子节点，并调用 of_register_spi_device 完成一个子节点的解析
					使用 spi_alloc_device 分配一个 SPI 设备
					使用 of_modalias_node 读取设备树的 compatible 来配置 SPI 设备的 modalias 参数
					使用 of_spi_parse_dt 解析设备树，并根据设备树配置 SPI 设备
					使用 spi_add_device 将设备注册到 SPI 总线
						检查片选是否合法
						调用 spi_dev_set_name ，利用总线名称和片选编号配置设备名称
						调用 bus_for_each_dev 遍历 SPI 设备，检查有设备是否存再片选相同问题（通过回调检查）
						绑定片选引脚
						调用 spi_setup 设置 SPI 设备
						调用 device_add 将设备加到系统（如果指定了总线会注册到对应总线，并进行设备驱动匹配，
					这里应该是 SPI 总线）
6、 SPI 驱动注册过程
	构造并初始化 struct spi_driver 对象
		调用 spi_register_driver  注册 SPI 驱动
			再次配置 struct spi_driver 对象，主要是内部 driver 成员的 bus 、 probe 、 remove 等
			调用 driver_register 注册驱动，这里指定的 bus 为 spi_bus_type 所以会注册到 spi_bus_type 总线中，
		然后执行设备驱动匹配操作
7、 SPI 设备传输数据
	SPI 设备采用 struct spi_transfer 描述一个数据包，其核心成员如下：
		//发送缓存
		const void *tx_buf;
		//接收缓存
		void *rx_buf;
		//缓存长度
		unsigned len;
		//此次传输完成后是否重新获取片选并配置
		unsigned cs_change;
		//片选无效时间
		u16 cs_change_delay;
		//cs_change_delay的单位，由us、ns、clk3种选择
		u8 cs_change_delay_unit;
		//字长
		u8 bits_per_word;
		//每个字传输完成后延迟多少us
		u8 word_delay_usecs;
		//每个字传输完成后延迟多少个时钟周期
		u16 word_delay;
		//在此传输后更改片选状态前之前延迟多少us
		u16 delay_usecs;
		//时钟速率
		u32 speed_hz;
		//spi_transfer链表节点
		struct list_head transfer_list;
	struct spi_transfer 描述的是单个数据包，在 SPI 框架中还需要利用 struct spi_message 将一个或多个数据包组合
成一个 message 后才能进行发送， struct spi_message 的核心成员如下：
		//要传输的数据包链表
		struct list_head transfers;
		//所属的 SPI 设备，其中包含了 SPI 设备的各种信息，如SPI控制器、片选、模式、时钟速率等
		struct spi_device *spi;
		//传输完成回调函数，用于异步传输
		void (*complete)(void *context);
		//用于再 SPI 控制器中形成一个队列，方便控制器管理需要发送的数据
		struct list_head queue;
	SPI 传输数据相关的函数如下：
		//初始化一个SPI message
		void spi_message_init(struct spi_message *m)
		//向SPI message中添加一个数据包
		void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
		//启动SPI，进行同步传输
		int spi_sync(struct spi_device *spi, struct spi_message *message)
		//启动SPI，进行异步传输，传输完成后调用void (*complete)(void *context)函数
		int spi_async(struct spi_device *spi, struct spi_message *message)
8、 SPI 设备树
	在那条 SPI 总线下挂载设备就在那条总线的设备树节点下添加对应设备的子节点，节点命名规则 [标签:]名称[@地址]
，节点内容必须包含 reg 属性、 compatible 属性、 spi-max-frequency 属性， reg 属性用于描述片选索引， compatible
属性用于设备和驱动的匹配， spi-max-frequency 用于描述设备可支持的最大 SPI 总线频率，如下是在 SPI1 中添加一个
icm20608 设备节点的示例：
&spi1 {
	//描述SPI控制器引脚和片选引脚，并使能I2C控制器
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&spi1_pins_a>;
	pinctrl-1 = <&spi1_sleep_pins_a>;
	cs-gpios = <&gpioz 3 GPIO_ACTIVE_LOW>;
	status = "okay";

	//描述icm20608设备
	spidev: icm20608@0 {
		compatible = "alientek,icm20608";
	 	reg = <0>;						/* CS #0 */
	 	spi-max-frequency = <8000000>;	/* 最大时钟频率 */
		spi-cpha;						/* cpha=1 */
		spi-cpol;						/* cpol=1 */
		spi-cs-high;					/* 片选高电平有效 */
		spi-lsb-first;					/* 低位在前 */
		spi-rx-bus-width = <1>;			/* 接收数据线宽度为1B */
		spi-tx-bus-width = <1>;			/* 发送数据线宽度为1B */
	};
};
9、 SPI 传输数据的流程
	以 spi_sync 为例， SPI 驱动框架提供了两种 spi_message 传输方案，分别如下
	方案1：
		构造 spi_message ，因为发送过程可能会使用DMA，所以内存最好使用 kmalloc 分配
			调用 spi_sync 进行传输
				再 spi_sync 中加锁后直接调用 __spi_sync （这里采用的互斥量加锁）
					调用 __spi_validate 检查 spi_message
					为 spi_message 绑定传输完成回调函数和 SPI 控制器
					检查 spi_controller 的 transfer 是否等于 spi_queued_transfer 函数，若不等于则调用
				spi_async_locked 函数
						然后在 spi_async_locked 中加锁后调用 __spi_async （这里采用的自旋锁加锁）
							通过 spi_controller 控制器中的 transfer 指针调用驱动层的传输函数（应该是一个非阻
						塞函数）
					调用 wait_for_completion 等待传输完成
					获取传输结果并返回
		驱动层的 transfer 函数大致实现方案：
			1、将 spi_message 放入一个的队列中，如果 SPI 控制器空闲则启动一个工作队列，然后立即返回
			2、利用 SPI 控制器中断结合工作队列完成对所有 spi_message 的传输，每传输完一个 spi_message 则调用
		相应的传输完成回调函数
	方案2：
		构造 spi_message ，因为发送过程可能会使用DMA，所以内存最好使用 kmalloc 分配
			调用 spi_sync 进行传输
				再 spi_sync 中加锁后直接调用 __spi_sync （这里采用的互斥量加锁）
					调用 __spi_validate 检查 spi_message
					为 spi_message 绑定传输完成回调函数和 SPI 控制器
					检查 spi_controller 的 transfer 是否等于 spi_queued_transfer ，等于则利用自旋锁加锁，然
				后调用 __spi_queued_transfer 将 spi_message 放入 spi_controller 的队列中
					然后调用 __spi_pump_messages 启动传输
						通过 spi_controller 的 cur_msg 判断是否正在传输，若正在传输则直接返回
						从 spi_controller 中取出的一个 spi_message ，并将其从队列中删除
						初始化硬件、 DMA 等
						配置硬件，映射 DMA 内存
						调用 spi_controller 控制器中的 transfer_one_message 传输一个 spi_message （默认的传
					输函数是 spi_transfer_one_message ）
							在 spi_transfer_one_message 函数中多次调用 spi_controller 的 transfer_one 去完成
						一个 spi_message 的传输（每次调用 transfer_one 后还需要调用 spi_transfer_wait 去等待
						transfer_one 传输完成）
							传输完成后再调用 spi_finalize_current_message
								在 spi_finalize_current_message 中启动一个内核线程传输剩余的 spi_message ，然
							后执行当前 spi_message 的传输完成回调函数
					调用 wait_for_completion 等待传输完成（若 __spi_pump_messages 函数是将信息传输完后返回的
				这里则无需等待）
					获取传输结果并返回
	驱动编写重点内容
		对于方案1，在写启动的时候重点考虑 transfer 函数的实现
		对于方案2，在写驱动的时候重点考虑 transfer_one 函数的实现（它用于传输一个 spi_transfer ，当传输完一个
	spi_transfer 后需要调用 spi_finalize_current_transfer 以表示传输结束），不过这里可以借助 spi_bitbang 用于
	简化驱动程序的设计，此时只需要提供 spi_bitbang 的 setup_transfer 函数、 txrx_bufs 函数、 chipselect 等函
	数即可，注意此方案不支持 spi_async 函数接口